<?php
namespace LowCode\Assets;

use Illuminate\Support\Collection;

class AssetBuilder
{

    /**
     * @var Collection
     */
    protected Collection $collection;

    /**
     * @var string
     */
    protected string $lastAddedAsset = '';

    /**
     * @var string
     */
    protected string $lastAddedType = '';

    /**
     * Constructor method.
     *
     * @return void
     */
    public function __construct()
    {
        $this->collection = new Collection(['css' => array(), 'js' => array()]);
    }

    /**
     * @param $assets
     * @param $namespace
     * @return $this
     */
    public function add($assets, $namespace = null): static
    {
        if (is_array($assets)) {
            foreach ($assets as $asset) {
                $this->add($asset, $namespace);
            }
        } elseif ($this->isAsset($assets)) {
            $this->addAsset($assets, $namespace);
        }

        return $this;
    }

    /**
     * Add a dependency to an asset.
     *
     * @param string $dependency
     * @return AssetBuilder
     */
    public function dependsOn(string $dependency): static
    {
        $collection = $this->collection->get($this->lastAddedType);

        foreach ($collection as $path => $item) {
            if ($path === $this->lastAddedAsset) {
                $collection[$path] = array(
                    'namespace'  => $item['namespace'],
                    'dependency' => $dependency
                );

                $this->collection->put($this->lastAddedType, $collection);
            }
        }

        return $this;
    }

    /**
     * Builds the CSS HTML tags.
     *
     * @return string
     */
    public function css(): string
    {
        $cssCollection = $this->sortDependencies($this->collection->get('css'), 'css');
        $output        = '';

        foreach ($cssCollection as $value) {
            $output .= '<link rel="stylesheet" href="'.$value.'">'."\n";
        }

        return $output;
    }

    /**
     * Builds the CSS HTML tags.
     *
     * @return string
     */
    public function js(): string
    {
        $jsCollection = $this->sortDependencies($this->collection->get('js'), 'js');
        $output       = '';

        foreach ($jsCollection as $value) {
            $output .= '<script type="text/javascript" src="'.$value.'"></script>'."\n";
        }

        return $output;
    }

    /**
     * Determines if the passed asset is indeed an asset.
     *
     * @param  string  $asset
     * @return bool
     */
    protected function isAsset(string $asset): bool
    {
        return $this->isJs($asset) || $this->isCss($asset);
    }

    /**
     * Determines if the passed asset is a Javascript file.
     *
     * @param string $asset
     * @return bool
     */
    protected function isJs(string $asset): bool
    {
        return str_contains($asset, '.js');
    }

    /**
     * Determines if the passed asset is a CSS file.
     *
     * @param  string  $asset
     * @return bool
     */
    protected function isCss(string $asset): bool
    {
        return str_contains($asset, '.css');
    }

    /**
     * Add an asset file to the collection.
     *
     * @param array|string $assets
     * @param string|null $namespace
     * @return AssetBuilder
     */
    protected function addAsset(array|string $assets, string $namespace = null): static
    {
        if (is_array($assets)) {
            foreach ($assets as $asset => $meta) {
                $this->addAsset($asset);
            }

            return $this;
        }

        $type       = ($this->isCss($assets)) ? 'css' : 'js';
        $collection = $this->collection->get($type);

        if (! in_array($assets, $collection)) {
            $collection[$assets] = array(
                'namespace'  => $namespace,
                'dependency' => ""
            );

            $this->collection->put($type, $collection);

            $this->lastAddedType  = $type;
            $this->lastAddedAsset = $assets;
        }

        return $this;
    }

    /**
     * Sorts the dependencies of all assets, to ensure dependant
     * assets are loaded first.
     *
     * @param array $assets
     * @param null $type
     * @return array
     */
    protected function sortDependencies(array $assets = array(), $type = null): array
    {
        $dependencyList = array();

        foreach ($assets as $key => $value) {
            if (isset($value['dependency'])) {
                $dependencyList[$key] = array($this->getNamespacedAsset($value['dependency'], $type));
            } else {
                $dependencyList[$key] = null;
            }
        }

        $dependencies = new AssetDeps($dependencyList, true);

        $sortedDependencies = $dependencies->sort();

        return array_filter($sortedDependencies);
    }


    /**
     * Retrievs the asset based on the defined namespace.
     *
     * @param string $namespace
     * @param string $type       (css|js)
     * @return string
     */
    protected function getNamespacedAsset(string $namespace, string $type = 'js'|'css'): string
    {
        $collection = $this->collection->get($type);

        foreach ($collection as $key => $value) {
            if ($value['namespace'] === $namespace) {
                return $key;
            }
        }
        return "";
    }
}
